﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;

namespace ECANPort
{
    /// <summary>
    /// Can波特率
    /// </summary>
    public enum CanBaud
    {
        B_1000K = 0,
        B_800K = 1,
        B_666K = 2,
        B_500K = 3,
        B_400K = 4,
        B_250K = 5,
        B_200K = 6,
        B_125K = 7,
        B_100K = 8,
        B_80K = 9,
        B_50K = 10,

    }

    public delegate void ECAN_RecvDataHandle(CAN_OBJ info, uint CanIndex);
    public delegate void ECAN_RecvStringDataHandle(string info, uint CanIndex);
    public delegate void ECAN_ReportErrHandle(CAN_ERR_INFO mErrInfo, uint CanIndex);
    public delegate void ECAN_ReportStringErrHandle(string mErrInfo, uint CanIndex);


    public class ECAN_Device : IECAN_Device
    {
        public BOARD_INFO BoardInfo { get; set; }
        public INIT_CONFIG InitConfig { get; set; }
        public CanComProc ComPort { get; set; }
        /// <summary>
        /// Can通道数量
        /// </summary>
        public uint[] CanIndex { get; private set; }
        public int CanCount { get { return CanIndex.Length; } }

        System.Timers.Timer tmrRead;
        bool _connected = false;

        private ECAN_Device(uint[] mCanIndex)
        {
            ComPort = new CanComProc(mCanIndex);
            ComPort.CanSendErr += ComPort_CanSendErr;
            ComPort.CanSendOK += ComPort_CanSendOK;
            CanIndex = mCanIndex;

            tmrRead = new System.Timers.Timer();
            tmrRead.Elapsed += TmrRead_Elapsed;

            tmrRead.Interval = 50;
        }

        private void ComPort_CanSendOK()
        {
            CanSendOK?.Invoke();
        }

        public event Action CanSendErr;
        public event Action CanSendOK;
        private void ComPort_CanSendErr()
        {
            CanSendErr?.Invoke();
        }

        public event ECAN_RecvDataHandle ECAN_RecvDataEvent;
        public event ECAN_RecvStringDataHandle ECAN_RecvStringDataEvent;

        public event ECAN_ReportErrHandle ECAN_ReportErrEvent;
        public event ECAN_ReportStringErrHandle ECAN_ReportStringErrEvent;


        private static ECAN_Device SignelDev;
        private static object LockObj = new object();
        public static ECAN_Device CreateDevice(uint[] mCanIndex)
        {
            if (SignelDev == null)
            {
                lock (LockObj)
                {
                    SignelDev = new ECAN_Device(mCanIndex);
                    return SignelDev;
                }
            }
            return SignelDev;
        }

        private void TmrRead_Elapsed(object sender, ElapsedEventArgs e)
        {
            for (uint i = 0; i < ComPort.HTBs.Length; i++)
            {
                ReadMessages(ref ComPort.HTBs[i]);
                ReadError(ref ComPort.HTBs[i]);
            }
        }

        private void ReadMessages(ref CanCom_HTB HTB)
        {

            CAN_OBJ frameinfo = new CAN_OBJ();
            int mCount = 0;

            while (HTB.gRecMsgBufHead != HTB.gRecMsgBufTail)
            {
                frameinfo = HTB.gRecMsgBuf[HTB.gRecMsgBufTail];
                HTB.gRecMsgBufTail += 1;
                if (HTB.gRecMsgBufTail >= CanComProc.REC_MSG_BUF_MAX)
                {
                    HTB.gRecMsgBufTail = 0;
                }

                // 数据报告
                ECAN_RecvDataEvent?.Invoke(frameinfo, HTB.CanIndex);
                ECAN_RecvStringDataEvent?.Invoke(frameinfo.ToString(), HTB.CanIndex);

                //ECAN_RecvStringDataEvent?.BeginInvoke(frameinfo.ToString(), HTB.CanIndex, null, null);
                mCount++;
                if (mCount >= 50)
                {
                    break;
                }
            }
        }

        private void ReadError(ref CanCom_HTB HTB)
        {
            CAN_ERR_INFO mErrInfo = new CAN_ERR_INFO();

            if (ECANDLL.ReadErrInfo(1, 0, HTB.CanIndex, out mErrInfo) == ECANStatus.STATUS_OK)
            {
                if (mErrInfo.ErrCode > 0)
                {

                    ECAN_ReportErrEvent?.Invoke(mErrInfo, HTB.CanIndex);
                    ECAN_ReportStringErrEvent?.Invoke(mErrInfo.ToString(), HTB.CanIndex);
                }
            }
            else
            {
                Console.WriteLine("Read Error Fault");
            }

        }

        /// <summary>
        /// 打开CAN设备 配置数组和通道数据长度需一致
        /// </summary>
        /// <param name="cfg">配置</param>
        /// <param name="CanIndex">Can通道好</param>
        /// <returns>通道全部启动成功返回True</returns>
        public bool OpenAndStartCAN_Device(INIT_CONFIG[] cfg, uint[] CanIndex)
        {
            this.CanIndex = CanIndex;

            if (ECANDLL.ECANDLL_LoadOk == false)
            {
                //try
                //{
                //    ECANDLL.CloseDevice(1, 0);
                //}
                //catch (Exception ex)
                //{
                //    return false;
                //}
                return false;
            }


            if (cfg != null && cfg.Length > 0 && CanIndex != null && CanIndex.Length > 0 && cfg.Length == CanIndex.Length)
            {
                if (_connected)
                {
                    CloseCAN_Device();
                }

                if (ECANDLL.OpenDevice(1, 0, 0) != ECANStatus.STATUS_OK)
                {
                    return false;
                }

                for (int i = 0; i < cfg.Length; i++)
                {
                    if (ECANDLL.InitCAN(1, 0, CanIndex[i], ref cfg[i]) != ECANStatus.STATUS_OK)
                    {
                        ECANDLL.CloseDevice(1, 0);
                        return false;
                    }
                }

                bool StartCANOk = false;
                for (int i = 0; i < cfg.Length; i++)
                {
                    if (ECANDLL.StartCAN(1, 0, CanIndex[i]) == ECANStatus.STATUS_OK)
                    {
                        Console.WriteLine($"Start CAN{CanIndex[i] + 1} Success");
                        StartCANOk = true;
                    }
                    else
                    {
                        StartCANOk = false;
                        Console.WriteLine($"Start Fault CAN{CanIndex[i] + 1}");
                    }
                }
                if (StartCANOk)
                {
                    tmrRead.Enabled = true;
                }
                ComPort.EnableProc = true;
                _connected = true;
                return StartCANOk;
            }
            else
                return false;
        }

        /// <summary>
        /// 关闭
        /// </summary>
        public ECANStatus CloseCAN_Device()
        {
            _connected = false;
            tmrRead.Enabled = false;
            this.ComPort.EnableProc = false;
            ClearSendBuff();
            ECANStatus status = ECANStatus.STATUS_ERR;
            if (ECANDLL.ECANDLL_LoadOk)
            {
                Task t1 = new Task(() =>
                {
                    try
                    {
                        System.Threading.Thread.Sleep(500);
                        status = ECANDLL.CloseDevice(1, 0);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                    Console.WriteLine("关闭设备");
                });
                t1.Start();
                t1.Wait(1000);
                return status;
            }
            return ECANStatus.STATUS_OK;
        }

        public void ClearSendBuff()
        {
            for (int i = 0; i < CanCount; i++)
            {
                ComPort.HTBs[i].gSendMsgBufTail = ComPort.HTBs[i].gSendMsgBufHead;
            }
        }

        public bool SendMsg(ref CanCom_HTB HTB, CAN_OBJ frameinfo)
        {
            if (_connected == false)
            {
                Console.WriteLine("Not open device!");
                return false;
            }

            HTB.gSendMsgBuf[HTB.gSendMsgBufHead] = frameinfo;
            HTB.gSendMsgBufHead += 1;
            if (HTB.gSendMsgBufHead >= CanComProc.SEND_MSG_BUF_MAX)
            {
                HTB.gSendMsgBufHead = 0;
            }
            return true;
        }

    }
}
